/* $Id: BlockCipher.java,v 1.1 2004/01/19 02:03:50 rgrimm Exp $
*
* Copyright (C) 1995-2000 The Cryptix Foundation Limited.
* All rights reserved.
*
* Use, modification, copying and distribution of this software is subject
* the terms and conditions of the Cryptix General Licence. You should have
* received a copy of the Cryptix General Licence along with this library;
* if not, you can download a copy from http://www.cryptix.org/ .
*/
package cryptix.jce.provider.cipher;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
/**
* <p>
* A fully constructed Cipher instance looks like this:
* <pre>
* +------------------------------------------+
* | CipherSpi (API methods) |
* | |
* | +--------------------------------------+ |
* | | Padding | |
* | | | |
* | | +----------------------------------+ | |
* | | | Mode | | |
* | | | | | |
* | | | +------------------------------+ | | |
* | | | | CipherSpi | | | |
* | | | | (blockcipher implementation) | | | |
* | | | | | | | |
* | | | +------------------------------+ | | |
* | | | | | |
* | | +----------------------------------+ | |
* | | | |
* | +--------------------------------------+ |
* | |
* +------------------------------------------+
* </pre>
*
* @author Jeroen C. van Gelderen (gelderen@cryptix.org)
* @author Paul Waserbrot (pw@cryptix.org)
* @version $Revision: 1.1 $
*/
public abstract class BlockCipher
extends CipherSpi
{
// Variables and constants
//............................................................................
/** State */
private int state = STATE_UNINITIALIZED;
private static final int
STATE_UNINITIALIZED = 0,
STATE_DECRYPT = 1,
STATE_ENCRYPT = 2;
/** Our blocksize */
private final int BLOCK_SIZE;
/** Our key. We need it when we reset ourselves. */
private Key key;
/** What algorithm is used */
private String algorithm;
private Padding padding;
private Mode mode;
// Constructors and Object() methods
//............................................................................
/**
* Construct a new BlockCipher (CipherSpi) with an zero-length name ""
* and given block size.
*/
protected BlockCipher(int blockSize)
{
this("", blockSize);
}
/**
* Construct a new BlockCipher (CipherSpi) with the given name and
* block size.
*/
protected BlockCipher(String algorithm, int blockSize)
{
super();
BLOCK_SIZE = blockSize;
this.algorithm = algorithm;
try
{
// set default mode and padding
this.mode = Mode.getInstance("ECB", this);
this.padding = Padding.getInstance("None", this.mode);
}
catch(NoSuchPaddingException e)
{
throw new InternalError(
"PANIC: Installation corrupt, default padding not available.");
}
catch(NoSuchAlgorithmException E)
{
throw new InternalError(
"PANIC: Installation corrupt, default mode not available.");
}
}
/**
* Always throws a CloneNotSupportedException (cloning of ciphers is not
* supported for security reasons).
*/
public final Object clone() throws CloneNotSupportedException
{
throw new CloneNotSupportedException();
}
// CipherSPI implementation
//............................................................................
protected final void engineSetMode(String mode)
throws NoSuchAlgorithmException
{
this.mode = Mode.getInstance( mode, this );
}
protected final void engineSetPadding(String padding)
throws NoSuchPaddingException
{
this.padding = Padding.getInstance( padding, this.mode );
}
protected final int engineGetBlockSize()
{
return padding.getBlockSize();
}
protected int engineGetKeySize(Key key)
throws InvalidKeyException
{
if( key==null )
throw new IllegalArgumentException("Key missing");
if( !key.getFormat().equalsIgnoreCase("RAW") )
throw new InvalidKeyException("Wrong format: RAW bytes needed");
byte[] userkey = key.getEncoded();
if(userkey == null)
throw new InvalidKeyException("RAW bytes missing");
return (userkey.length * 8);
}
/**
* Returns the length in bytes that an output buffer would need to be in
* order to hold the result of the next update or doFinal operation, given
* the input length <code>inputLen</code> (in bytes).
*
* This call takes into account any unprocessed (buffered) data from a
* previous update call(s), and padding.
*
* The actual output length of the next <code>update or doFinal</code> call
* may be smaller than the length returned by this method. For ciphers with
* a padding, calling the update method will generally return less data
* than predicted by this function.
*
* @param inputLen the length in bytes.
*
* @return the maximum amount of data that the cipher will return.
*/
protected final int engineGetOutputSize(int inputLen)
{
return padding.getOutputSize(inputLen);
}
/**
* Returns a copy of the initialization vector (IV) used in this cipher.
*
* @return A copy of the IV or null if this cipher does not have an IV or
* null if the IV has not yet been set.
*/
protected final byte[] engineGetIV()
{
return padding.getIV();
}
protected final AlgorithmParameters engineGetParameters()
{
AlgorithmParameterSpec aps = padding.getParamSpec();
if (aps == null)
return (AlgorithmParameters) null;
// Fix the parameters
AlgorithmParameters ap = null;
try
{
ap = AlgorithmParameters.getInstance(algorithm, "CryptixCrypto");
ap.init(aps);
}
catch(InvalidParameterSpecException e)
{
throw new RuntimeException("PANIC: Unreachable code reached.");
}
catch(NoSuchAlgorithmException e)
{
throw new RuntimeException("PANIC: Unreachable code reached.");
}
catch(NoSuchProviderException e)
{
throw new RuntimeException("PANIC: Unreachable code reached.");
}
return ap;
}
/**
* Initialize this blockcipher for encryption or decryption.
*
* If the cipher requires randomness, it is taken from <code>random</code>.
* Randomness is required for modes that use IVs and might be required for
* some padding schemes.
*
* @param opmode Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE.
* @param key secret key
* @param random source of randomness
*/
protected final void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException
{
AlgorithmParameterSpec aps = padding.getParamSpec();
try
{
this.engineInit(opmode, key, aps, random);
}
catch (InvalidAlgorithmParameterException e)
{
throw new InternalError("Unreachable code reached.");
}
}
protected final void
engineInit(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
boolean decrypt = ( opmode==Cipher.DECRYPT_MODE ); // FIXME?
padding.init(decrypt, key, params, random);
}
protected final void // FIXME: add this to cipher suite?? (pw)
engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
throw new RuntimeException("Not supported on this cipher.");
}
protected final int
engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException
{
if (inputLen == 0)
return 0;
return padding.update(input, inputOffset, inputLen,
output, outputOffset);
}
/** Implemented in terms of engineUpdate(byte[], int, int, byte[], int) */
protected final byte[]
engineUpdate(byte[] input, int inputOffset, int inputLen)
{
if (inputLen == 0)
return null;
try
{
byte[] tmp = new byte[this.engineGetOutputSize(inputLen)];
int i = this.engineUpdate(input, inputOffset, inputLen, tmp, 0);
if (i != tmp.length)
{
byte [] t = new byte[i];
System.arraycopy(tmp, 0, t, 0, i);
tmp = t;
}
return tmp;
}
catch(ShortBufferException e)
{
throw new RuntimeException("PANIC: Unreachable code reached.");
}
}
/**
* @throws BadPaddingException
* (decryption only) if padding is expected but not found at the
* end of the data or the padding is found but corrupt
* @throws IllegalBlockSizeException
* if no padding is specified and the input data is not a multiple
* of the blocksize.
* @throws ShortBufferException if the given buffer is to short to hold
* the result.
*/
protected final int
engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
{
return padding.doFinal(input, inputOffset, inputLen,
output, outputOffset);
}
/**
* Implemented in terms of engineDoFinal(byte[], int, int, byte[], int)
*
* @throws BadPaddingException
* (decryption only) if padding is expected but not found at the
* end of the data.
* @throws IllegalBlockSizeException
* if no padding is specified and the input data is not a multiple
* of the blocksize.
*/
protected final byte[]
engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException
{
try
{
byte[] tmp = new byte[this.engineGetOutputSize(inputLen)];
int i = this.engineDoFinal(input, inputOffset, inputLen, tmp, 0);
if (i != tmp.length)
{
byte [] t = new byte[i];
System.arraycopy(tmp, 0, t, 0, i);
tmp = t;
}
return tmp;
}
catch(ShortBufferException e)
{
throw new RuntimeException("PANIC: Unreachable code reached.");
}
}
// Abstract BPI methods
//
// The abstract methods are overridden by the actual cipher implementations
// The core* methods are called from within the Mode* classes
//.............................................................................
abstract void coreInit(Key key, boolean decrypt)
throws InvalidKeyException;
/**
* Encrypt a given buffer. in and out can point to the same buffer if
* (outOffset == inOffset) || (outOffset >= (inOffset+coreGetBlockSize))
* That is: the buffers may not partially overlap...
*/
abstract void coreCrypt(byte[] in, int inOffset, byte[] out, int outOffset);
int coreGetBlockSize()
{
return BLOCK_SIZE;
}
}